package gu.sql2java;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Objects;
import java.util.regex.Pattern;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;

public abstract class BaseFuzzyMatchFilter<K> implements IFuzzyMatchFilter<K> {

	protected MatchErrorHandler<K> errorHandler;

	public BaseFuzzyMatchFilter() {
	}

	@Override
	public IFuzzyMatchFilter<K> withErrorHandler(MatchErrorHandler<K> errorHandler) {
		this.errorHandler = errorHandler;
		return this;
	}
	public static class DefaultFuzzyFilter<K> extends BaseFuzzyMatchFilter<K>{
		private K key;
		@Override
		public boolean apply(K input) {
			return Objects.equals(key, input);
		}
		@Override
		public IFuzzyMatchFilter<K> withPattern(K pattern, int matchFlags) {
			key = pattern;
			return this;
		}
	}

	/**
	 * 线程安全(thread safe)的模糊查询匹配接口实现基类
	 * @author guyadong
	 *
	 */
	public static abstract class BaseStringMatchFilter extends BaseFuzzyMatchFilter<String>implements IStringMatchFilter{
		private static final ThreadLocal<Pattern> pattern = new ThreadLocal<>();
		private String patternStr;
		private Function<String, String> patternFormater;
		private int patternFlag = 0;
	
		@Override
		public BaseStringMatchFilter withPattern(String pattern,int... flags) {
			try{
				checkArgument(!Strings.isNullOrEmpty(pattern),"pattern is null or empty");
				this.patternStr = pattern;
				if(patternFormater != null){
					patternStr = patternFormater.apply(pattern);
				}
				if(flags != null){
					for(int flag : flags){
						patternFlag |= flag;
					}
				}
				return this;
			} catch (RuntimeException e) {
				if(errorHandler != null){
					errorHandler.onMatchError(e, pattern);
				}
				throw e;
			}
		}
		@Override
		public BaseStringMatchFilter withPattern(String pattern, int matchFlags){
			return withPattern(pattern,new int[]{matchFlags});
		}
	
		protected boolean isInCaseSensitive(){	
			return (patternFlag & Pattern.CASE_INSENSITIVE) != 0;
		}
		protected Pattern pattern() {
			if(pattern.get() == null || !pattern.get().pattern().equals(patternStr)){
				pattern.set(Pattern.compile(checkNotNull(patternStr,"patternStr is uninitialized"),patternFlag));
			}
			return pattern.get();
		}
	
		public Function<String, String> getPatternFormater() {
			return patternFormater;
		}
	
		public BaseStringMatchFilter setPatternFormater(Function<String, String> patternFormater) {
			this.patternFormater = patternFormater;
			return this;
		}
		
	}

	public static class RegexFilter extends BaseFuzzyMatchFilter.BaseStringMatchFilter{
		@Override
		public boolean apply(String input) {
			try{
				return input == null 
						? false 
						: pattern().matcher(input).matches();
			} catch (RuntimeException e) {
				if(errorHandler != null){
					errorHandler.onMatchError(e, pattern().pattern());
				}
				return false;
			}
		}
	}

	static class DefaultStringMatcher extends BaseFuzzyMatchFilter.BaseStringMatchFilter{
		private final Predicate<String> filter;
		public DefaultStringMatcher(StringMatchType matchType) {
			switch (checkNotNull(matchType,"matchType is null")) {
			case CMP_LEFT_MATCH:
				filter = new Predicate<String>() {					
					@Override
					public boolean apply(String input) {
						if(isInCaseSensitive()){
							input.toUpperCase().startsWith(pattern().pattern().toUpperCase());
						}
						return input.startsWith(pattern().pattern());
					}
				};
				break;
			case CMP_RIGHT_MATCH:
				filter = new Predicate<String>() {
					
					@Override
					public boolean apply(String input) {
						if(isInCaseSensitive()){
							return input.toUpperCase().endsWith(pattern().pattern().toUpperCase());
						}						
						return input.endsWith(pattern().pattern());
					}
				};
				break;
			case CMP_MATCH:
				filter = new Predicate<String>() {
					
					@Override
					public boolean apply(String input) {
						if(isInCaseSensitive()){
							return input.toUpperCase().indexOf(pattern().pattern().toUpperCase())>=0;
						}
						return input.indexOf(pattern().pattern())>=0;
					}
				};
				break;
			case EXACTLY_MATCH:
				filter = new Predicate<String>() {
					
					@Override
					public boolean apply(String input) {
						if(isInCaseSensitive()){
							return input.toUpperCase().equals(pattern().pattern().toUpperCase());
						}
						return input.equals(pattern().pattern());
					}
				};
				break;
			default:				
				throw new IllegalArgumentException("UNSUPPORTED MatchType " + matchType);
			}
		}
	
		@Override
		public boolean apply(String input) {
			try{
				return input == null 
						? false 
						: filter.apply(input);
			} catch (RuntimeException e) {
				if(errorHandler != null){
					errorHandler.onMatchError(e, pattern().pattern());
				}
				return false;
			}
		}		
	}
}
